﻿using System;
using System.Collections.Generic;
using System.Text;

namespace TTLL.Asm
{
    public class FromText
    {
        public static TTLL.Asm.Module LoadModule(params string[] file)
        {
            Module module = Module.CreateEmpty();
            List<AST.AstDoc> docs = new List<AST.AstDoc>();
            foreach (var f in file)
            {
                var ast = AST.AstDoc.Parse(f);
                //FirstColl(f);
                docs.Add(ast);
            }

            FillASM(module, docs);
            return module;
        }
        static string GetConstStr(NativeTypeEX type, byte[] value)
        {
            var strname = type.ToString();
            for (var i = 0; i < value.Length; i++)
                strname += value[i].ToString("x02");
            return strname;
        }
        static void FillASM(TTLL.Asm.Module module, List<AST.AstDoc> astdocs)
        {
            List<string> allconst = new List<string>();
            //处理const 并且收集 struct 和 func的名字
            foreach (var d in astdocs)
            {
                foreach (var n in d.nodes)
                {
                    AST.ConstAst c = n as AST.ConstAst;
                    AST.Struct s = n as AST.Struct;
                    AST.Function f = n as AST.Function;
                    if (c != null)
                    {
                        var type = NativeTypeEX.FromString(c.type);
                        var data = type.DataFromString(c.value);

                        var cstr = GetConstStr(type, data);

                        if (allconst.Contains(cstr) == false)
                        {
                            allconst.Add(cstr);
                            module.constTable.Add(new ConstValue()
                            {
                                type = type,
                                data = data
                            });
                            module.constNameTable.Add(c.name);
                        }
                    }
                    if (s != null)
                    {
                        if (module.structNameTable.Contains(s.name))
                        {
                            throw new Exception("repeat struct name got:" + s.name);
                        }
                        var _struct = Struct.CreateEmpty();
                        module.structTable.Add(_struct);
                        module.structNameTable.Add(s.name);
                        foreach (var m in s.members)
                        {
                            AST.TypeValue typev = m as AST.TypeValue;
                            if (typev != null)
                            {
                                _struct.memberNameTable.Add(typev.name);
                                VarDefine define = new VarDefine();
                                _struct.members.Add(define);//type此时不完整，下一遍填充
                            }
                        }
                    }
                    if (f != null)
                    {
                        if (module.funcNameTable.Contains(f.name))
                        {
                            throw new Exception("repeat function name got:" + f.name);
                        }
                        var _func = Function.CreateEmpty();
                        module.funcTable.Add(_func);
                        module.funcNameTable.Add(f.name);
                        foreach (var tv in f._params)
                        {
                            _func.paramNameTable.Add(tv.name);
                            _func.Params.Add(new VarDefine());//type 此时不完整，下一遍填充
                        }
                    }
                }
            }


            foreach (var d in astdocs)
            {
                foreach (var n in d.nodes)
                {
                    AST.Struct s = n as AST.Struct;
                    AST.Function f = n as AST.Function;
                    if (s != null)
                    {//member 充填
                        var index = module.structNameTable.IndexOf(s.name);
                        var _struct = module.structTable[index];
                        foreach (var item in s.head)
                        {
                            AST.Attribute astatt = item as AST.Attribute;
                            if (item == null) continue;
                            Attribute att = new Attribute();
                            FillAttr(module, att, astatt);
                            _struct.attributes.Add(att);
                        }
                        int i = 0;
                        foreach (var item in s.members)
                        {
                            AST.TypeValue typev = item as AST.TypeValue;
                            if (typev != null)
                            {
                                var MItem = _struct.members[i];
                                MItem.type = NativeTypeEX.FromString(typev.type);

                                if (typev.value != null)
                                {
                                    if (module.constNameTable.Contains(typev.value))
                                    {
                                        MItem.initConstValue = (uint)module.constNameTable.IndexOf(typev.value) + 1;
                                    }
                                    else
                                    {
                                        var data = MItem.type.DataFromString(typev.value);
                                        var cstr = GetConstStr(MItem.type, data);
                                        MItem.initConstValue = (uint)allconst.IndexOf(cstr) + 1;
                                    }
                                }

                                i++;
                            }
                        }
                    }
                    if (f != null)
                    {
                        var index = module.funcNameTable.IndexOf(f.name);
                        var _func = module.funcTable[index];
                        foreach (var item in f.head)
                        {
                            AST.Attribute astatt = item as AST.Attribute;
                            if (item == null) continue;
                            Attribute att = new Attribute();
                            FillAttr(module, att, astatt);
                            _func.attributes.Add(att);
                        }
                        //return value & params
                        _func.Return.type = module.GetTypeFromStr(f.returntype);
                        for (var i = 0; i < f._params.Count; i++)
                        {
                            _func.Params[i].type = module.GetTypeFromStr(f._params[0].type);
                            if (f._params[i].value != null)
                                _func.Params[i].initConstValue = module.GetConstFromStr(f._params[i].value);
                        }
                        var astbody = f.body;
                        var asmbody = _func.Body;
                        FillASM_Body(module, _func, astbody, asmbody, 0xff);

                    }
                }
            }
        }

        private static void FillASM_Body(Module module, Function _func, AST.Body astbody, Body asmbody, byte depth)
        {
            foreach (var astitem in astbody.body)
            {
                if (astitem is AST.TypeValue)
                {
                    var ast = astitem as AST.TypeValue;
                    uint _const = module.GetConstFromStr(ast.value);
                    var _type = module.GetTypeFromStr(ast.type);
                    _func.Variabes.Add(new VarDefine() { type = _type, initConstValue = _const });
                    _func.varNameTable.Add(ast.name);
                }
                else if (astitem is AST.Comment)
                {
                    Instruction inst = new Instruction();
                    inst.instParams = new List<InstParam>();

                    var ast = astitem as AST.Comment;
                    inst.code = OpCode.Skip;
                    //原來的設計 instParam 存的東西有點太少
                    inst.instParams.Add(new InstParam() { type = InstParamType.ImNumber, value = System.Text.Encoding.UTF8.GetBytes(ast.text) });
                    asmbody.body.Add(inst);
                }
                else if (astitem is AST.Instruction)
                {
                    Instruction inst = new Instruction();
                    inst.parent = asmbody;
                    FillASMInstruction(module, _func, astitem, inst, depth);

                    asmbody.body.Add(inst);

                }
            }
        }

        private static void FillASMInstruction(Module module, Function _func, AST.INode astitem, Instruction inst, byte depth)
        {
            inst.instParams = new List<InstParam>();

            var ast = astitem as AST.Instruction;
            var b = Enum.TryParse<OpCode>(ast.opcode, true, out inst.code);
            if (!b) throw new Exception("unknown opcode:" + ast.opcode);
            if (ast.retvalue != null)
            {
                var id = (byte)(_func.varNameTable.IndexOf(ast.retvalue) + 1);
                //必然是一個變量引用
                inst.instParams.Add(new InstParam() { type = InstParamType.ReturnVar, value = new byte[] { id } });
            }


            if (inst.code == OpCode.If)
            {
                inst.IfCondition = new Instruction();
                FillASMInstruction(module, _func, ast.if_condition, inst.IfCondition, depth);
                inst.Body = new Body();
                inst.Body.parent = inst.parent;
                FillASM_Body(module, _func, ast.block_body, inst.Body, depth);
            }
            else if (inst.code == OpCode.Block)
            {
                if (depth == 0)
                    throw new Exception("error depth");
                inst.Body = new Body();
                inst.Body.parent = inst.parent;
                inst.Body.BlockLabel = ast.block_label;
                inst.Body.BlockDepth =(byte)(depth - 1);
                FillASM_Body(module, _func, ast.block_body, inst.Body, (byte)(depth - 1));
            }

            if (inst.code == OpCode.Break || inst.code == OpCode.Continue)
            {
                var body = inst.parent;

                if (int.TryParse(ast._params[0], out int v))
                {
                    while (body != null)
                    {
                        if(body.BlockDepth == v)
                        {
                            inst.instParams.Add(new InstParam() { type = InstParamType.BlockLayer, value = new byte[] { body.BlockDepth } });
                            break;
                        }
                        body = body.parent;
                    }
                }
                else
                {
                    string name = ast._params[0];
                    while (body != null)
                    {
                        if (body.BlockLabel == name)
                        {
                            inst.instParams.Add(new InstParam() { type = InstParamType.BlockLayer, value = new byte[] { body.BlockDepth } });
                            break;
                        }
                        body = body.parent;
                    }
                }

                //參數特殊
            }
            else
            {
                foreach (var p in ast._params)
                {
                    //暫時比較偷懶，都限制256個
                    var idVar = (byte)(_func.varNameTable.IndexOf(p) + 1);
                    var idArg = (byte)(_func.paramNameTable.IndexOf(p) + 1);
                    var idConst = (byte)(module.constNameTable.IndexOf(p) + 1);
                    if (idVar > 0)
                    {
                        inst.instParams.Add(new InstParam() { type = InstParamType.Var, value = new byte[] { idVar } });
                    }
                    else if (idArg > 0)
                    {
                        inst.instParams.Add(new InstParam() { type = InstParamType.Param, value = new byte[] { idArg } });
                    }
                    else if (idConst > 0)
                    {
                        inst.instParams.Add(new InstParam() { type = InstParamType.Const, value = new byte[] { idConst } });
                    }
                    else
                    {
                        byte[] imnumber = ParseImNumber2Bytes(p);
                        inst.instParams.Add(new InstParam() { type = InstParamType.ImNumber, value = imnumber });
                    }
                    //否則就是立即數了
                }
            }
            //if cond
        }

        static byte[] ParseImNumber2Bytes(string str)
        {
            if (str.Contains("."))//float
            {
                var num = double.TryParse(str, out double v);

                return BitConverter.GetBytes(v);
            }
            else //int 正負號，16進制，2進制
            {
                if (str.IndexOf("0x") == 0 || str.IndexOf("0X") == 0)
                {
                    var len = (str.Length - 2) / 2;
                    var data = new byte[len];
                    for (var i = 2; i < str.Length; i += 2)
                    {
                        data[len - 1 - (i - 2) / 2] = byte.Parse(str.Substring(i, 2), System.Globalization.NumberStyles.HexNumber);
                    }
                    return data;
                }
                else if (str[0] == '-')
                {
                    if (Int64.TryParse(str, out Int64 num))
                    {
                        if (num <= Int32.MaxValue && num >= Int32.MinValue)
                        {
                            return BitConverter.GetBytes((Int32)num);
                        }
                        return BitConverter.GetBytes(num);
                    }
                    throw new Exception("error parse.");
                }
                else
                {
                    if (UInt64.TryParse(str, out UInt64 num))
                    {
                        if (num <= UInt32.MaxValue)
                        {
                            return BitConverter.GetBytes((UInt32)num);
                        }

                        return BitConverter.GetBytes(num);
                    }
                    throw new Exception("error parse.");
                }
            }
        }
        static void FillAttr(TTLL.Asm.Module module, Attribute att, AST.Attribute astatt)
        {
            att.name = astatt.name;
            if (astatt.values.Count == 0)
            {
                return;
            }
            att.values = new List<string>();

            Dictionary<string, string> mapatr = new Dictionary<string, string>();
            var nonamecount = 0;
            foreach (var v in astatt.values)
            {
                if (v.name == null)
                    nonamecount++;
                else
                {
                    mapatr[v.name] = v.value;
                }
            }
            if (nonamecount != 0 && nonamecount != astatt.values.Count)
                throw new Exception("some attribute params with name,some no,all name or all not.");

            if (nonamecount == 0)
            {//named
             //找到那个结构体
             //然后按照结构体的名字，填上默认值，有名字的替换掉
                var sindex = module.structNameTable.IndexOf(att.name);
                var ss = module.structTable[sindex];
                for (var i = 0; i < ss.members.Count; i++)
                {
                    var key = ss.memberNameTable[i];
                    if (mapatr.ContainsKey(key))
                        att.values.Add(mapatr[key]);
                    else
                        att.values.Add(null);
                }
            }
            else
            {//按索引找const 换上
                foreach (var i in astatt.values)
                {
                    att.values.Add(i.value);
                }
            }


        }
    }
}
